package com.gitee.wsl.jvm.okhttp.request

import com.gitee.wsl.ext.okio.base.CallState
import com.gitee.wsl.jvm.okio.sink.CountingSink
import kotlinx.coroutines.channels.SendChannel
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.Request
import okhttp3.RequestBody
import okio.Buffer
import okio.BufferedSink
import okio.buffer
import okio.source
import java.io.IOException
import java.io.InputStream


/**
 *
 * @FileName:
 *          cn.netdiscovery.http.core.utils.extension.`RequestBody+Extension`
 * @author: Tony Shen
 * @date: 2021/11/17 3:46 下午
 * @version: V1.0 <描述当前版本功能>
 */
fun <T> RequestBody.flatMap(transform: (String) -> T): T = transform(
    Buffer().use {
        writeTo(it)
        it.readUtf8()
    }
)

class UpLoadRequestBody(val inputStream: InputStream): RequestBody(){
    override fun contentType(): MediaType? = "application/octet-stream".toMediaTypeOrNull()

    override fun contentLength(): Long {
        return try {
            // 返回输入流的长度，如果无法确定长度，返回-1
            inputStream.available().toLong()
        } catch (e: IOException) {
            -1
        }
    }

    override fun writeTo(sink: BufferedSink) {
        // 将输入流中的数据写入到请求体中
        inputStream.use { inputStream ->
            sink.writeAll(inputStream.source())
        }
    }
}



/**
 * A [RequestBody] that replaces the original one to count the number of bytes uploaded.
 */
class CountingRequestBody(
    val delegate: RequestBody,
    private val channel: SendChannel<CallState.Uploading>
) : RequestBody() {

    var bytesUploaded: Long = 0

    fun injectTo(request: Request): Request =
        request.newBuilder().method(request.method, this).build()

    override fun contentType(): MediaType? = delegate.contentType()

    override fun contentLength(): Long = delegate.contentLength()

    override fun writeTo(sink: BufferedSink) {
        val countingSink = CountingSink(sink, contentLength(), channel)

        val countingBufferedSink = countingSink.buffer()
        delegate.writeTo(countingBufferedSink)
        if (countingBufferedSink.isOpen) {
            countingBufferedSink.emit()
        }

        bytesUploaded = countingSink.bytesUploaded
    }

    override fun isOneShot(): Boolean = delegate.isOneShot()

    // CountingRequestBody cannot be duplex.
    override fun isDuplex(): Boolean = false
}